{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# torch 神经网络\n",
    "# \n",
    "''' \n",
    "可以使用torch.nn包来构建神经网络.\n",
    "我们已经介绍了autograd包, nn包则依赖于autograd包来定义模型并对它们求导。\n",
    "一个nn.Module包含各个层和一个forward(input)方法, 该方法返回output\n",
    "\n",
    "一个神经网络的典型训练过程如下： \n",
    "定义包含一些可学习参数(或者叫权重）的神经网络, 在输入数据集上迭代通过网络处理输入计算 loss (输出和正确答案的距离),\n",
    "将梯度反向传播给网络的参数更新网络的权重, 一般使用一个简单的规则: weight = weight - learning_rate * gradient\n",
    "'''\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "\n",
    "\n",
    "class Net(nn.Module):\n",
    "    def __init__(self): \n",
    "        super().__init__()\n",
    "        # 输入图像channel：1；输出channel：6；5x5卷积核\n",
    "        self.conv1 = nn.Conv2d(1, 6, 5)\n",
    "        self.conv2 = nn.Conv2d(6, 16, 5)\n",
    "        # an affine operation: y = Wx + b\n",
    "        self.fc1 = nn.Linear(16 * 5 * 5, 120)\n",
    "        self.fc2 = nn.Linear(120, 84)\n",
    "        self.fc3 = nn.Linear(84, 10)\n",
    "        return\n",
    "\n",
    "    ''' \n",
    "    我们只需要定义 forward 函数, backward函数会在使用autograd时自动定义, backward函数用来计算导数。\n",
    "    '''\n",
    "    def forward(self, x):\n",
    "        # 2x2 Max pooling\n",
    "        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))\n",
    "        # 如果是方阵,则可以只使用一个数字进行定义\n",
    "        x = F.max_pool2d(F.relu(self.conv2(x)), 2)\n",
    "        x = x.view(-1, self.num_flat_features(x))\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = self.fc3(x)\n",
    "        return x\n",
    "\n",
    "\n",
    "    def num_flat_features(self, x):\n",
    "        size = x.size()[1:]\n",
    "        # 除去批处理维度的其他所有维度\n",
    "        num_features = 1 \n",
    "        for s in size:\n",
    "            num_features *= s\n",
    "        return num_features\n",
    "\n",
    "# \n",
    "# 使用模型 Net\n",
    "# \n",
    "net = Net()\n",
    "print(net)\n",
    "print()\n",
    "\n",
    "# 一个模型的可学习参数可以通过net.parameters()返回\n",
    "print('# 一个模型的可学习参数可以通过net.parameters()返回')\n",
    "params = list(net.parameters())\n",
    "print(len(params)) \n",
    "print(params[0].size())\n",
    "print()\n",
    "\n",
    "# 让我们尝试一个随机的 32x32 的输入\n",
    "print('# 让我们尝试一个随机的 32x32 的输入')\n",
    "input = torch.randn(1, 1, 32, 32)\n",
    "# print(input)\n",
    "output = net(input) \n",
    "print(output)\n",
    "print()\n",
    "\n",
    "# 清零所有参数的梯度缓存，然后进行随机梯度的反向传播：\n",
    "# net.zero_grad() \n",
    "# output.backward(torch.randn(1, 10))\n",
    "\n",
    "# 损失函数\n",
    "print('# 损失函数')\n",
    "target = torch.randn(10)        # 本例子中使用模拟数据\n",
    "target = target.view(1, -1)     # 使目标值与数据值尺寸一致\n",
    "criterion = nn.MSELoss()\n",
    "loss = criterion(output, target)\n",
    "print(loss)\n",
    "print()\n",
    "\n",
    "# 反向传播\n",
    "print('# 调用loss.backward()来反向传播误差')\n",
    "net.zero_grad()  # 清零所有参数(parameter）的梯度缓存\n",
    "print('conv1.bias.grad before backward') \n",
    "print(net.conv1.bias.grad)\n",
    "loss.backward()\n",
    "print('conv1.bias.grad after backward') \n",
    "print(net.conv1.bias.grad)\n",
    "print()\n",
    "\n",
    "# 更新权重\n",
    "# 在使用神经网络时，可能希望使用各种不同的更新规则，如 SGD、Nesterov-SGD、Adam、RMSProp等。\n",
    "# 为此，我们构建了一个较小的包` torch.optim`，它实现了所有的这些方法.\n",
    "# 创建优化器(optimizer）\n",
    "optimizer = optim.SGD(net.parameters(), lr=0.01)\n",
    "# 在训练的迭代中：\n",
    "optimizer.zero_grad()   # 清零梯度缓存\n",
    "output = net(input)\n",
    "loss = criterion(output, target)\n",
    "loss.backward()\n",
    "optimizer.step()    # 更新参数\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "60d0b561bcf179bd0cd76361af4313e00281c3c5daca1903769e6cc3dd82b37c"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
